package com.gmrz.uaf.crypto.internal.algos;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.*;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DEROctetString;

import com.gmrz.uaf.crypto.CryptoConstants;

public class UAFAuthAlgRSADer extends UAFAuthAlg {
    private Provider bouncyCastleProvider;

    private static final Logger logger = LogManager.getLogger(UAFAuthAlgRSADer.class);

    public UAFAuthAlgRSADer(CryptoConstants.UAFAlgSign authAlgorithmSuite) {
        super(authAlgorithmSuite);
    }

    public boolean init() {
        this.bouncyCastleProvider = Security.getProvider("BC");

        boolean bouncyCastleAvailable = this.bouncyCastleProvider != null;
        boolean signAlgoAvailable = false;
        Signature sig = null;
        try {
            sig = Signature.getInstance(this.authAlgorithmSuite.getSignatureAlgo());
        } catch (NoSuchAlgorithmException e) {

        }
        if (null != sig) {
            signAlgoAvailable = true;
        }

        return (bouncyCastleAvailable) && (signAlgoAvailable);
    }

    public boolean verifySignature(PublicKey publicKey, byte[] signedData, byte[] signature)
            throws GeneralSecurityException {
        logger.info("UAFAuthAlgRSADer verifySignature");
        long start = System.currentTimeMillis();
        Signature sig = Signature.getInstance(this.authAlgorithmSuite.getSignatureAlgo());

        sig.initVerify(publicKey);
        sig.update(signedData);
        try {
            ByteArrayInputStream inStream = new ByteArrayInputStream(signature);
            ASN1InputStream asnInputStream = new ASN1InputStream(inStream);
            ASN1Primitive derObject = asnInputStream.readObject();

            if ((derObject == null) || (!(derObject instanceof DEROctetString))) {
                throw new GeneralSecurityException("Signature has not been encoded in DER Octet String format.");
            }

            DEROctetString derOctetString = (DEROctetString) derObject;
            byte[] decoded = derOctetString.getOctets();

            if ((decoded == null) || (decoded.length != 256)) {
                throw new GeneralSecurityException("Decoded RSA Raw signature is not of correct length.");
            }
            logger.debug("UAFAuthAlgRSADer verifySignature time:" + (System.currentTimeMillis() - start) + " ms");
            return sig.verify(decoded);
        } catch (IOException e) {
            throw new GeneralSecurityException("Exception while verifying signature.", e);
        }
    }
}
